﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Framework.Helper.Helpers.CurrentLimitingHelpers
{
    /// <summary>
    /// 精确时间窗口限流控制器
    /// </summary>
    /// <remarks>
    /// 功能特性：
    /// <para>
    /// 不同时间窗口内的任务异步执行，互不干扰，上一周期内的任务未执行完成也不会影响下一周期的任务
    /// </para>
    /// <para>1. 严格保证每秒不超过最大请求数</para>
    /// <para>2. 非阻塞设计，单个请求不会阻塞队列</para>
    /// <para>3. 线程安全，支持高并发场景</para>
    /// <para>4. 资源高效，无复杂令牌计算</para>
    /// </remarks>
    internal sealed class CurrentLimitingASync : IDisposable
    {
        /// <summary>
        /// 限流器分组名称，用于日志区分
        /// </summary>
        private readonly string _groupName;

        /// <summary>
        /// 每秒最大允许的请求数量
        /// </summary>
        private readonly int _maxRequestsPerSecond;

        /// <summary>
        /// 时间窗口长度（固定为1秒）
        /// </summary>
        private readonly TimeSpan _timeWindow = TimeSpan.FromSeconds(1);

        /// <summary>
        /// 线程安全请求队列（先进先出）
        /// </summary>
        private readonly ConcurrentQueue<CurrentLimitingModel> _queue = new();

        /// <summary>
        /// 定时处理队列的Timer（间隔100ms）
        /// </summary>
        private readonly Timer _processTimer;

        /// <summary>
        /// 高精度计时器，用于测量时间窗口
        /// </summary>
        private readonly Stopwatch _stopwatch = Stopwatch.StartNew();

        /// <summary>
        /// 当前时间窗口内已处理的请求计数
        /// </summary>
        private int _requestCount;

        /// <summary>
        /// 资源释放标记
        /// </summary>
        private bool _disposed;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="maxRequestsPerSecond">每秒最大请求数（必须大于0）</param>
        /// <param name="groupName">限流器分组名称（可选）</param>
        /// <exception cref="ArgumentException">当maxRequestsPerSecond小于等于0时抛出</exception>
        public CurrentLimitingASync(int maxRequestsPerSecond)
        {
            // 参数校验
            if (maxRequestsPerSecond <= 0)
                throw new ArgumentException("最大请求数必须大于0", nameof(maxRequestsPerSecond));

            _maxRequestsPerSecond = maxRequestsPerSecond;

            // 初始化处理定时器（每100ms触发一次处理逻辑）
            // 注意：这不是限流的时间窗口，只是处理队列的频率
            _processTimer = new Timer(
                callback: ProcessRequests,
                state: null,
                dueTime: 100,
                period: 100);
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="maxRequestsPerSecond">每秒最大请求数（必须大于0）</param>
        /// <param name="groupName">限流器分组名称（可选）</param>
        public CurrentLimitingASync(int maxRequestsPerSecond, string groupName = null)
        {
            // 参数校验
            if (maxRequestsPerSecond > 0)
            {
                _maxRequestsPerSecond = 1000 / maxRequestsPerSecond;
                _groupName = groupName ?? Guid.NewGuid().ToString();

                // 初始化处理定时器（每100ms触发一次处理逻辑）
                // 注意：这不是限流的时间窗口，只是处理队列的频率
                _processTimer = new Timer(callback: ProcessRequests, state: null, dueTime: 100, period: 100);
            }
        }

        /// <summary>
        /// 将请求加入限流队列
        /// </summary>
        /// <param name="action">需要执行的请求操作</param>
        /// <returns>请求执行结果（同步等待）</returns>
        /// <exception cref="ObjectDisposedException">当限流器已释放时抛出</exception>
        /// <remarks>
        /// 此方法会阻塞调用线程直到请求被执行并返回结果
        /// 注意：实际API调用是在后台线程执行的
        /// </remarks>
        public string EnqueueRequest(Func<string> action)
        {
            if (_disposed)
                throw new ObjectDisposedException(nameof(CurrentLimitingSync));

            // 创建任务完成源用于同步等待结果
            var tcs = new TaskCompletionSource<string>();

            // 将请求加入队列
            _queue.Enqueue(new CurrentLimitingModel
            {
                Action = action,
                ResultTcs = tcs
            });

            // 同步等待任务完成（会阻塞当前线程）
            return tcs.Task.GetAwaiter().GetResult();
        }

        /// <summary>
        /// 定时触发的请求处理方法
        /// </summary>
        /// <param name="state">Timer状态对象（未使用）</param>
        /// <remarks>
        /// 此方法每100ms执行一次，负责：
        /// 1. 检查并重置时间窗口
        /// 2. 从队列中取出请求并执行（不超过当前时间窗口的剩余配额）
        /// </remarks>
        private void ProcessRequests(object state)
        {
            try
            {
                // 检查时间窗口是否需要重置
                var elapsedSeconds = _stopwatch.Elapsed.TotalSeconds;
                if (elapsedSeconds >= 1.0)
                {
                    // 重置计数器和计时器
                    Interlocked.Exchange(ref _requestCount, 0);
                    _stopwatch.Restart();
                    //Console.WriteLine($"{DateTime.Now}：{_groupName} -> 重置计数器和计时器，重新开始下一秒的请求量限制");
                }

                // 计算当前还可处理的请求数量
                int remainingQuota = _maxRequestsPerSecond - _requestCount;
                // 处理队列中的请求（不超过剩余配额）
                while (remainingQuota > 0 && _queue.TryDequeue(out var request))
                {
                    // 增加已处理计数
                    Interlocked.Increment(ref _requestCount);
                    remainingQuota--;

                    // 在后台线程执行实际请求（避免阻塞处理线程）
                    Task.Run(() =>
                    {
                        try
                        {
                            // 执行请求并设置结果
                            var result = request.Action();
                            request.ResultTcs.SetResult(result);
                        }
                        catch (Exception ex)
                        {
                            // 捕获异常并传递回调用方
                            request.ResultTcs.SetException(ex);
                        }
                    });
                    Console.WriteLine($"{DateTime.Now}：{_groupName} -> 当前秒已经执行的任务数量：{_requestCount}/{_maxRequestsPerSecond}，剩余的请求数量：{remainingQuota}/{_maxRequestsPerSecond}");
                }
            }
            catch (Exception ex)
            {
                // 记录但不抛出异常，避免中断定时器
                Debug.WriteLine($"[{_groupName}] 请求处理发生错误: {ex}");
            }
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        /// <remarks>
        /// 1. 停止定时器
        /// 2. 取消所有未处理的请求
        /// 3. 标记为已释放
        /// </remarks>
        public void Dispose()
        {
            if (_disposed) return;

            _disposed = true;

            // 停止处理定时器
            _processTimer?.Dispose();

            // 取消所有未处理的请求
            while (_queue.TryDequeue(out var request))
            {
                request.ResultTcs.SetCanceled();
            }
        }
    }
}
